package com.log4jviewer.filters;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import com.log4jviewer.filters.FilterItemModel.FieldType;
import com.log4jviewer.filters.FilterItemModel.LogicOperand;

/**
 * Loading XML config file and getting filter list
 * 
 * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
 */

public class ConfigFilterLoader {

    public List<FilterModel> loadFilters(final String file) throws ParserConfigurationException, SAXException,
            IOException {
        // get a factory
        SAXParserFactory saxParsFactory = SAXParserFactory.newInstance();
        // get a new instance of parser
        SAXParser saxParser = saxParsFactory.newSAXParser();
        // parse the file and also register FilterConfigXmlParser class for call backs
        FilterConfigXmlParser filterConfigXmlParser = new FilterConfigXmlParser();
        saxParser.parse(file, filterConfigXmlParser);
        return filterConfigXmlParser.getFilters();
    }

    private class FilterConfigXmlParser extends DefaultHandler {

        private List<FilterModel> filters;

        private FilterItemModel filterItemModel;

        // Values from XML file to set filter and item fields.
        private String value;

        public FilterConfigXmlParser() {
            filters = new ArrayList<FilterModel>();
        }

        public List<FilterModel> getFilters() {
            return filters;
        }

        @Override
        public void startElement(final String uri, final String localName, final String qName,
                final Attributes attributes)
                throws SAXException {
            value = "";
            if (FilterXmlConstants.RULE.equals(qName)) {
                // add a new Filter
                filters.add(new FilterModel());
            } else if (FilterXmlConstants.RULE_ITEM.equals(qName)) {
                // create a new Item (rule)
                filterItemModel = new FilterItemModel();
            }
        }

        @Override
        public void characters(final char[] ch, final int start, final int length) throws SAXException {
            value = new String(ch, start, length);
        }

        @Override
        public void endElement(final String uri, final String localName, final String qName) throws SAXException {
            if (FilterXmlConstants.NAME.equals(qName)) {
                int filterIndex = filters.size() - 1;
                filters.get(filterIndex).setFilterName(value);

            } else if (FilterXmlConstants.DESCRIPTION.equals(qName)) {
                int filterIndex = filters.size() - 1;
                filters.get(filterIndex).setFilterDescr(value);

            } else if (FilterXmlConstants.RULE_ITEM.equals(qName)) {
                int filterIndex = filters.size() - 1;
                // add a new item to created filter
                filters.get(filterIndex).addItem(filterItemModel);

            } else if (FilterXmlConstants.LOGIC_OPERAND.equals(qName)) {
                LogicOperand logicOp = validateAndGetLogicOperand(value);
                filterItemModel.setLogicOperand(logicOp);

            } else if (FilterXmlConstants.FIELD_TYPE.equals(qName)) {
                FieldType fieldType = validateAndGetFieldType(value);
                filterItemModel.setFieldType(fieldType);

            } else if (FilterXmlConstants.INCLUDE.equals(qName)) {
                filterItemModel.setInclude(Boolean.parseBoolean(value));

            } else if (FilterXmlConstants.MATCH_PATTERN.equals(qName)) {
                filterItemModel.setPattern(value);

            } else if (FilterXmlConstants.ENABLE.equals(qName)) {
                filterItemModel.setEnabled(Boolean.parseBoolean(value));
            }
        }

        private LogicOperand validateAndGetLogicOperand(final String value) {
            for (LogicOperand logic : FilterItemModel.LogicOperand.values()) {
                if (logic.getValue().equals(value)) {
                    return logic;
                }
            }
            throw new IllegalArgumentException("Current logic operand: " + value
                    + ". Only one of these logic operands is available: or|and");
        }

        private FieldType validateAndGetFieldType(final String value) {
            for (FieldType field : FilterItemModel.FieldType.values()) {
                if (field.getValue().equals(value)) {
                    return field;
                }
            }
            throw new IllegalArgumentException("Current field type: " + value
                    + ". Only one of these field types is available: level|message|category|line|date|ndc|throwable");
        }
    }
}